using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Security;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using Microsoft.SharePoint;
#if MOSS
using Microsoft.SharePoint.Publishing;
#endif
using Microsoft.SharePoint.WebPartPages;
using PS.Practices.Provisioner;
using PS.Practices.SiteBuilder.Features.API;
using PS.Practices.SiteBuilder.Features.Schema.DOM;
using PS.Practices.Util;

namespace PS.Practices.SiteBuilder.Features.Schema.DOM
{
    

    [XmlRoot("Provisioner")]
    public class ProvisionerRoot
    {
        public class MasterPageActions
        {
            public static string NoneAction = "{{None}}";
            public static string SetToParent = "{{Parent}}";
            public static string SetToRoot = "{{Root}}";
        }

        private List<WebOperation> webOperations;
        //private List<FixupFiles> fixupFiles;w
        //private FixupLists fixupLists;

        [XmlAttribute]
        public string ShowErrors { get; set; }

        [XmlElement(typeof(FixupLists))]
        [XmlElement(typeof(RestrictSiteTemplates))]
        [XmlElement(typeof(RestoreWebProperties))]
        [XmlElement(typeof(CustomizedCssFiles))]
        [XmlElement(typeof(SetStyling))]
        [XmlElement(typeof(ProvisionWebs))]
        [XmlElement(typeof(FixupWebACL))]
        [XmlElement(typeof(FixupFiles))]
        [XmlElement(typeof(ProvisionLists))]
        [XmlElement(typeof(WebFeatures))]
        public List<WebOperation> Operations
        {
            get { return webOperations; }
            set { webOperations = value; }
        }

        [XmlAttribute]
        public string ApplyWebTemplate { get; set; }

        private string setMasterPage = MasterPageActions.NoneAction;

        [XmlAttribute]
        public string MasterUrl
        {
            get { return setMasterPage; }
            set { setMasterPage = value; }
        }

        private string setCustomMasterPage = MasterPageActions.NoneAction;

        [XmlAttribute]
        public string CustomMasterUrl
        {
            get { return setCustomMasterPage; }
            set { setCustomMasterPage = value; }
        }

        [XmlAttribute]
        public bool ReopenWebOnApplyWebTemplate { get; set; }

        //[XmlElement(Namespace = "asd")]
        //public List<FixupFiles> FixupFiles
        //{
        //    get { return fixupFiles; }
        //    set { fixupFiles = value; }
        //}

        //[XmlElement]
        //public FixupLists FixupLists
        //{
        //    get { return fixupLists; }
        //    set { fixupLists = value; }
        //}

        public void Process(SPWeb web)
        {
            SPWeb priorWeb = null;
            try
            {
                if (!string.IsNullOrEmpty(ApplyWebTemplate))
                {
                    web.ApplyWebTemplate(ApplyWebTemplate);

                    //SPWebTemplateCollection collWebTemplates = web.Site.RootWeb.GetAvailableWebTemplates(1033);
                    //SPWebTemplate spWebTemplate = null;
                    //foreach (SPWebTemplate template in collWebTemplates)
                    //{
                    //    if (template.Name.ToLowerInvariant().Contains(ApplyWebTemplate.ToLowerInvariant()))
                    //    {
                    //        spWebTemplate = template;
                    //        break;
                    //    }
                    //}

                    //web.ApplyWebTemplate(spWebTemplate);

                    if (ReopenWebOnApplyWebTemplate)
                    {
                        //SP2010 Publishing Feature fix. reopen the web after the call to ApplyWebTemplate to avoid an update conflict with entries 
                        //added by the Prov feature activation sink into the web's property bag 
                        priorWeb = web;
                        var webId = web.ID;
                        web = web.Site.OpenWeb(webId);
                    }
                }

                #region MasterUrl

                bool update = false;
                if (!string.IsNullOrEmpty(MasterUrl) &&
                    !MasterUrl.Equals(MasterPageActions.NoneAction, StringComparison.InvariantCultureIgnoreCase))
                {
                    update = true;
                    string masterPageUrl = MasterUrl;

                    if (MasterUrl.Equals(MasterPageActions.SetToRoot, StringComparison.InvariantCultureIgnoreCase))
                    {
                        using (SPWeb root = web.Site.OpenWeb(web.Site.ServerRelativeUrl))
                        {
                            masterPageUrl = root.MasterUrl;
                        }
                    }
                    else if (MasterUrl.Equals(MasterPageActions.SetToParent, StringComparison.InvariantCultureIgnoreCase))
                    {
                        using (SPWeb parentWeb = web.Site.OpenWeb(web.ParentWebId))
                        {
                            masterPageUrl = parentWeb.MasterUrl;
                        }
                    }
                    else
                    {

                        masterPageUrl = masterPageUrl;
                        web.CustomMasterUrl = masterPageUrl;

                    }

                    web.MasterUrl = masterPageUrl;

                }

                #endregion

                #region CustomMasterUrl

                if (!string.IsNullOrEmpty(CustomMasterUrl) &&
                    !CustomMasterUrl.Equals(MasterPageActions.NoneAction, StringComparison.InvariantCultureIgnoreCase))
                {
                    update = true;
                    string customMasterPageUrl = CustomMasterUrl;

                    if (CustomMasterUrl.Equals(MasterPageActions.SetToRoot, StringComparison.InvariantCultureIgnoreCase))
                    {
                        using (SPWeb root = web.Site.OpenWeb(web.Site.ServerRelativeUrl))
                        {
                            customMasterPageUrl = root.CustomMasterUrl;
                        }
                    }
                    else if (CustomMasterUrl.Equals(MasterPageActions.SetToParent,
                                                    StringComparison.InvariantCultureIgnoreCase))
                    {
                        using (SPWeb parentWeb = web.Site.OpenWeb(web.ParentWebId))
                        {
                            customMasterPageUrl = parentWeb.CustomMasterUrl;
                        }
                    }

                    web.CustomMasterUrl = customMasterPageUrl;
                }

                if (update)
                    web.Update();

                #endregion

                Process(web, webOperations);
            } finally
            {
                if (priorWeb != null && web!= null)
                {
                    web.Dispose();
                    web = priorWeb; //restore original web for the callers.. the upstream code should not be calling web.Update() as this will result in the update conflict
                }
            }
        }

        public static void Process(SPWeb web, IEnumerable<WebOperation> webOperations)
        {
            foreach (WebOperation webOperation in webOperations)
            {
                webOperation.Execute(web);
            }
        }

        //public void FixupFile(File file, SPWeb web)
        //{
        //    file.Fixup(web);
        //}
    }

    public class Extension
    {
        private string assembly;
        private string tagPrefix;

        [XmlAttribute()]
        public string Assembly
        {
            get { return assembly; }
            set { assembly = value; }
        }

        [XmlAttribute()]
        public string TagPrefix
        {
            get { return tagPrefix; }
            set { tagPrefix = value; }
        }


        public Extension(string assembly, string tagPrefix)
        {
            Assembly = assembly;
            TagPrefix = tagPrefix;
        }
    }

    public class WebFeatures : WebOperation
    {
        private List<string> _features;
        [XmlElement("Feature")]
        public List<string> Features
        {
            get
            {
                return _features;
            }
            set
            {
                _features = value;
            }
        }

        public override void Execute(SPWeb webParam)
        {
            using (SPSite site = new SPSite(webParam.Url))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    foreach (string feature in Features)
                    {
                        Guid ftrGuid = new Guid(feature.Trim());
                        SPFeature ftr = web.Features[ftrGuid];

                        if (ftr == null)
                        {
                            web.Features.Add(ftrGuid);
                        }
                    }

                    web.AllowUnsafeUpdates = true;
                    web.Update();
                    web.AllowUnsafeUpdates = false;
                }
            }
        }
    }

    public class Feature
    {
        [XmlAttribute("ID")]
        public string FeatureID
        { get; set; }
    }

    #region FixupWebACL web action

    public class FixupWebACL : WebOperation
    {
        private bool inheritTopNav;
        private bool respectInheritanceSetting;
        private string roleAssignments;
        private string syncGroupQuickLaunch;
        private List<WebACLAction> webACLActions = new List<WebACLAction>();

        [XmlAttribute]
        public bool InheritTopNav
        {
            [DebuggerStepThrough]
            get { return inheritTopNav; }

            [DebuggerStepThrough]
            set { inheritTopNav = value; }
        }

        [XmlAttribute]
        public bool RespectInheritanceSetting
        {
            [DebuggerStepThrough]
            get { return respectInheritanceSetting; }

            [DebuggerStepThrough]
            set { respectInheritanceSetting = value; }
        }

        [XmlAttribute]
        public string RoleAssignments
        {
            [DebuggerStepThrough]
            get { return roleAssignments; }

            [DebuggerStepThrough]
            set { roleAssignments = value; }
        }

        [XmlElement(typeof(AssociateSiteGroup))]
        [XmlElement(typeof(SiteGroup))]
        public List<WebACLAction> Operations
        {
            get { return webACLActions; }
            set { webACLActions = value; }
        }

        [XmlAttribute]
        public string SyncGroupQuickLaunch
        {
            [DebuggerStepThrough]
            get { return syncGroupQuickLaunch; }

            [DebuggerStepThrough]
            set { syncGroupQuickLaunch = value; }
        }

        public override void Execute(SPWeb web)
        {
            PSWebAcl webAcl = new PSWebAcl(web);
            
           // Logger.Info("Configuring web ACL");

            webAcl.ChangeInheritTopNav(InheritTopNav);
            
//             if (this.InheritTopNav != web.Navigation.UseShared)
//             {
//                 web.Navigation.UseShared = inheritTopNav;
//                 web.Update();
//             }

            if (this.RespectInheritanceSetting && !(web.HasUniqueRoleDefinitions || web.HasUniqueRoleAssignments))
            {
                Logger.Info("RespectInheritanceSetting setting is set or assumed as TRUE and the web does not seem to have unique permissions specified. the FixupWebACL actions shall be skipped.");
                return;
            }

            webAcl.ChangeRoleAssignments(RoleAssignments);

//             if (!string.IsNullOrEmpty(this.RoleAssignments))
//             {
//                 if (this.RoleAssignments.StartsWith("Break"))
//                 {
//                     Logger.Info("Breaking role inheritance: " + this.RoleAssignments);
//                     try
//                     {
//                         web.BreakRoleInheritance(this.RoleAssignments == "BreakAndCopy");
//                     }
//                     catch (Exception e)
//                     {
//                         Logger.Info("Error while braking web role inheritance: " + this.RoleAssignments);
//                         Logger.Error(e);
//                     }
//                 }
//             }

            foreach (WebACLAction webACLAction in Operations)
            {
                webACLAction.Process(web);
            }

            ProcessQuickLaunch(web);

            web.Update();
        }

        private void ProcessQuickLaunch(SPWeb web)
        {
            if (!string.IsNullOrEmpty(this.SyncGroupQuickLaunch))
            {
                PSWebAcl webAcl = new PSWebAcl(web);
                switch(SyncGroupQuickLaunch)
                {
                    case "False":
                    case "false":
                        return;

                    case "FromAssociations":
                    case "FromAllGroups":
                    case "True":
                    case "true":
                    {
                        webAcl.SyncGroupQuickLaunch(SyncGroupQuickLaunch);
                        //Logger.Info("Syncing group quick launch");
                        //AclUtil.SyncGroupQuickLaunch(web, SyncGroupQuickLaunch == "FromAssociations");
                        break;
                    }

                    case "FromList":
                    {
                        //List<SPPrincipal> groups = new List<SPPrincipal>();
                        List<string> groups = new List<string>();
                        foreach (WebACLAction action in webACLActions)
                        {
                            if (action is AssociateSiteGroup)
                            {
                                AssociateSiteGroup siteGroup = (AssociateSiteGroup)action;

                                if (!string.IsNullOrEmpty(siteGroup.AddToQuickLaunch))
                                {
                                    bool addToQuickLaunch;
                                    if (bool.TryParse(siteGroup.AddToQuickLaunch, out addToQuickLaunch))
                                    {
                                        if(addToQuickLaunch)
                                        {
                                            groups.Add(siteGroup.Name);
                                            //SPGroup group = siteGroup.GetGroup();
                                            //if (group != null)
                                            //{
                                            //    groups.Add(group);
                                            //}
                                        }
                                    }
                                }
                            }
                        }


                        webAcl.SyncGroupQuickLaunch(groups);
                        //AclUtil.SyncGroupQuickLaunch(web, groups);

                        break;
                    }

                        
                }
                
            }
        }
    }

    public abstract class WebACLAction
    {
        public abstract void Process(SPWeb web);
    }

    public class FbaRole : WebACLAction
    {
        private string action;
        private string name;

        [XmlAttribute]
        public string Action
        {
            [DebuggerStepThrough]
            get { return action; }

            [DebuggerStepThrough]
            set { action = value; }
        }

        [XmlAttribute]
        public string Name
        {
            [DebuggerStepThrough]
            get { return name; }

            [DebuggerStepThrough]
            set { name = value; }
        }

        public override void Process(SPWeb web)
        {
            switch (this.Action)
            {
                case "Create":
                    Roles.Provider.CreateRole(ParserUtil.ReplaceLiveFields(name, web));
                    break;
                case "Delete":
                    Logger.Info("Deleting cross site group: " + this.Name);
                    Roles.Provider.DeleteRole(ParserUtil.ReplaceLiveFields(name, web), true); 
                    break;
                default:
                    Logger.Info("Unrecognized action: " + this.Action);
                    break;
            }
        }
    }

    public class SiteGroup : WebACLAction
    {
        private string action;
        private string name;
        private string description;
        private bool allowMembersEditMembership;
        private bool allowRequestToJoinLeave;
        private bool autoAcceptRequestToJoinLeave;
        private bool onlyAllowMembersViewMembership;
        private string requestToJoinLeaveEmailSetting;
        private string owner;

        [XmlAttribute]
        public string Action
        {
            [DebuggerStepThrough]
            get { return action; }

            [DebuggerStepThrough]
            set { action = value; }
        }

        [XmlAttribute]
        public string Name
        {
            [DebuggerStepThrough]
            get { return name; }

            [DebuggerStepThrough]
            set { name = value; }
        }

        [XmlAttribute]
        public string Description
        {
            [DebuggerStepThrough]
            get { return description; }

            [DebuggerStepThrough]
            set { description = value; }
        }

        [XmlAttribute()]
        public bool AllowMembersEditMembership
        {
            [DebuggerStepThrough]
            get { return allowMembersEditMembership; }

            [DebuggerStepThrough]
            set { allowMembersEditMembership = value; }
        }

        [XmlAttribute()]
        public bool AllowRequestToJoinLeave
        {
            [DebuggerStepThrough]
            get { return allowRequestToJoinLeave; }

            [DebuggerStepThrough]
            set { allowRequestToJoinLeave = value; }
        }

        [XmlAttribute()]
        public bool AutoAcceptRequestToJoinLeave
        {
            [DebuggerStepThrough]
            get { return autoAcceptRequestToJoinLeave; }

            [DebuggerStepThrough]
            set { autoAcceptRequestToJoinLeave = value; }
        }

        [XmlAttribute()]
        public bool OnlyAllowMembersViewMembership
        {
            [DebuggerStepThrough]
            get { return onlyAllowMembersViewMembership; }

            [DebuggerStepThrough]
            set { onlyAllowMembersViewMembership = value; }
        }

        [XmlAttribute()]
        public string RequestToJoinLeaveEmailSetting
        {
            [DebuggerStepThrough]
            get { return requestToJoinLeaveEmailSetting; }

            [DebuggerStepThrough]
            set { requestToJoinLeaveEmailSetting = value; }
        }

        [XmlAttribute()]
        public string Owner
        {
            [DebuggerStepThrough]
            get { return owner; }

            [DebuggerStepThrough]
            set { owner = value; }
        }

        public override void Process(SPWeb web)
        {
            switch (this.Action)
            {
                case "Create":
                    CreateGroup(web);
                    break;
                case "Delete":
                    Logger.Info("Deleting cross site group: " + this.Name);
                    AclUtil.DeleteWebGroup(this.Name, web);
                    break;
                default:
                    Logger.Info("Unrecognized action: " + this.Action);
                    break;
            }
        }

        private void CreateGroup(SPWeb web)
        {
            Logger.Info("Creating cross site group: " + this.Name);

            PSGroup group = new PSGroup(Name, Description, web);
            
            group.AllowMembersEditMembership = AllowMembersEditMembership;
            group.AllowRequestToJoinLeave = AllowRequestToJoinLeave;
            group.AutoAcceptRequestToJoinLeave = AutoAcceptRequestToJoinLeave;
            group.OnlyAllowMembersViewMembership = OnlyAllowMembersViewMembership;
            group.RequestToJoinLeaveEmailSetting = RequestToJoinLeaveEmailSetting;
            group.SetOwner(Owner);
            group.Update();
        

            //TODO: Check that this.Description can be null
            //SPGroup group = AclUtil.CreateWebGroup(this.Name, this.Description, web);
            //if (group != null)
            //{
            //    group.AllowMembersEditMembership = AllowMembersEditMembership;
            //    group.AllowRequestToJoinLeave = AllowRequestToJoinLeave;
            //    group.AutoAcceptRequestToJoinLeave = AutoAcceptRequestToJoinLeave;
            //    group.OnlyAllowMembersViewMembership = OnlyAllowMembersViewMembership;
                
            //    if(!string.IsNullOrEmpty(RequestToJoinLeaveEmailSetting))
            //    {
            //        group.RequestToJoinLeaveEmailSetting = 
            //            ParserUtil.ReplaceLiveFields(RequestToJoinLeaveEmailSetting, web);
            //    }

            //    if(!string.IsNullOrEmpty(Owner))
            //    {
            //        Owner = ParserUtil.ReplaceLiveFields(Owner, web);
            //        using (SPWeb rootWeb = web.Site.RootWeb)
            //        {
            //            SPUser user = null;
            //            try
            //            {
            //                user = rootWeb.Users[Owner];
            //            }
            //            catch (SPException)
            //            {
            //            }

            //            if (user != null)
            //            {
            //                group.Owner = user;
            //            }
            //            else
            //            {
            //                SPGroup owners = null;
            //                try
            //                {
            //                    owners = rootWeb.SiteGroups[Owner];
            //                }
            //                catch (SPException)
            //                {
            //                }

            //                if (owners != null)
            //                {
            //                    group.Owner = owners;
            //                }
            //            }
            //        }
            //    }

            //    group.Update();
            //}
            
        }
    }

    public class User : WebACLAction
    {
        private string action;
        private string loginName;
        private string displayName;
        private string email;
        private string description;

        [XmlAttribute]
        public string Action
        {
            [DebuggerStepThrough]
            get { return action; }

            [DebuggerStepThrough]
            set { action = value; }
        }

        [XmlAttribute]
        public string LoginName
        {
            [DebuggerStepThrough]
            get { return loginName; }

            [DebuggerStepThrough]
            set { loginName = value; }
        }

        [XmlAttribute]
        public string DisplayName
        {
            [DebuggerStepThrough]
            get { return displayName; }

            [DebuggerStepThrough]
            set { displayName = value; }
        }

        [XmlAttribute]
        public string Email
        {
            [DebuggerStepThrough]
            get { return email; }

            [DebuggerStepThrough]
            set { email = value; }
        }

        [XmlAttribute]
        public string Description
        {
            [DebuggerStepThrough]
            get { return description; }

            [DebuggerStepThrough]
            set { description = value; }
        }

        public override void Process(SPWeb web)
        {
            if (string.IsNullOrEmpty(this.DisplayName))
                this.DisplayName = this.LoginName;

            PSWebAcl webAcl = new PSWebAcl(web);

            switch (this.Action)
            {
                case "Add":
                    webAcl.AddWebUser(LoginName, DisplayName, Email, Description);

                    //Logger.Info("Adding user record: " + loginName);
                    //SPUser user = AclUtil.ResolveUser(loginName, web);
                    
                    //if (user == null)
                    //{
                    //    //TODO: Check that this.Description can be null
                    //    if (!string.IsNullOrEmpty(this.Email)/* && !string.IsNullOrEmpty(this.Description)*/)
                    //    {
                    //        web.AllUsers.Add(this.LoginName, this.Email, this.DisplayName, this.Description);
                    //    }
                    //}
                    //else
                    //{
                    //    if (displayName != loginName)
                    //        user.Name = displayName;
                    //    if (email != null)
                    //        user.Email = email;
                    //    if (description != null)
                    //        user.Notes = description;
                    //    user.Update();
                    //}
                    break;
                case "Remove":
                    webAcl.RemoveWebUser(LoginName);
                    //Logger.Info("Removing user from site: " + this.LoginName);
                    //try
                    //{
                    //    web.AllUsers.Remove(this.LoginName);
                    //}
                    //catch (SPException)
                    //{
                    //    Logger.Info("Could not remove ");
                    //}
                    break;
                default:
                    Logger.Info("Unrecognized action: " + this.Action);
                    break;
            }

        }
    }

    public class AssociateSiteGroup : WebACLAction
    {
        private string role;
        private string name;
        private string addToQuickLaunch;
        private SPWeb web;

        [XmlAttribute]
        public string Role
        {
            [DebuggerStepThrough]
            get { return role; }

            [DebuggerStepThrough]
            set { role = value; }
        }

        [XmlAttribute]
        public string Name
        {
            [DebuggerStepThrough]
            get { return name; }

            [DebuggerStepThrough]
            set { name = value; }
        }

        [XmlAttribute]
        public string AddToQuickLaunch
        {
            [DebuggerStepThrough]
            get { return addToQuickLaunch; }

            [DebuggerStepThrough]
            set { addToQuickLaunch = value; }
        }

        public override void Process(SPWeb web)
        {
            if (string.IsNullOrEmpty(Role) || string.IsNullOrEmpty(Name))
                return;
            
            PSWebAcl webAcl = new PSWebAcl(web);
            this.web = web;

            webAcl.AssociateSiteGroup(Name, Role);

            //SPGroup group = GetGroup();
            //if (group == null)
            //{
            //    return;
            //}

            //switch (role)
            //{
            //    case "Owner":
            //        Logger.Info("Associating group " + group.Name + " for role " + role);
            //        web.AssociatedOwnerGroup = group;
            //        role = "Full Control";
            //        break;

            //    case "Member":
            //        Logger.Info("Associating group " + group.Name + " for role " + role);
            //        web.AssociatedMemberGroup = group;
            //        role = "Contribute";
            //        break;

            //    case "Visitor":
            //        Logger.Info("Associating group " + group.Name + " for role " + role);
            //        web.AssociatedVisitorGroup = group;
            //        role = "Read";
            //        break;

            //}

            //SPRoleDefinition roleDef;
            //try
            //{
            //    roleDef = web.RoleDefinitions[role];
            //}
            //catch (SPException)
            //{
            //    Logger.Info("Unrecognized role: " + role);
            //    return;
            //}

            //SPRoleAssignment assignment = new SPRoleAssignment(group);
            //assignment.RoleDefinitionBindings.Add(roleDef);
            //web.RoleAssignments.Add(assignment);
            //web.Update();
        }

        //public SPGroup GetGroup()
        //{
        //    name = ParserUtil.ReplaceLiveFields(name, web);
        //    return AclUtil.ResolveSiteGroup(name, web);
        //}
    }

    public class PrincipalRole : WebACLAction
    {
        private string role;
        private string name;
        private string type;
        private string action;

        [XmlAttribute]
        public string Role
        {
            [DebuggerStepThrough]
            get { return role; }

            [DebuggerStepThrough]
            set { role = value; }
        }

        [XmlAttribute]
        public string Name
        {
            [DebuggerStepThrough]
            get { return name; }

            [DebuggerStepThrough]
            set { name = value; }
        }

        [XmlAttribute]
        public string Type
        {
            [DebuggerStepThrough]
            get { return type; }

            [DebuggerStepThrough]
            set { type = value; }
        }

        [XmlAttribute]
        public string Action
        {
            [DebuggerStepThrough]
            get { return action; }

            [DebuggerStepThrough]
            set { action = value; }
        }

        public override void Process(SPWeb web)
        {
            bool doAdd = action == "Add";
            bool doRemove = action == "Remove";

            if (!doAdd && !doRemove)
            {
                Logger.Info("Unsupported action attribute: " + action);
                return;
            }

            PSAcl acl = new PSAcl(web, web);

            switch (type)
            {
                case "SiteGroup":
                    {
                        if (doAdd)
                            acl.AddSiteGroup(name, role);
                        else
                            acl.RemoveSiteGroup(name, role);

                        break;
                    }

                case "User":
                    {
                        if (doAdd)
                            acl.AddUser(name, role);
                        else
                            acl.RemoveUser(name, role);

                        break;
                    }

                case "FbaRole":
                    {
                        if (doAdd)
                            acl.AddFbaRole(name, role);
                        else
                            acl.RemoveFbaRole(name, role);

                        break;
                    }

                default:
                    Logger.Info("Unsupported principal type : " + type);
                    break;
            }

            //name = ParserUtil.ReplaceLiveFields(name, web);

            //SPPrincipal spPrincipal = type == "SiteGroup" ? AclUtil.ResolveSiteGroup(name, web)
            //                              : (SPPrincipal)AclUtil.ResolveUser(name, web);
            //if (spPrincipal == null)
            //{
            //    return;
            //}

            //SPRoleDefinition definition;
            //try
            //{
            //    definition = web.RoleDefinitions[role];
            //}
            //catch (SPException)
            //{
            //    Logger.Info("Unrecognized role: " + role);
            //    return;
            //}

            //try
            //{
            //    if (action == "Add")
            //    {
            //        SPRoleAssignment assignment2 = new SPRoleAssignment(spPrincipal);
            //        assignment2.RoleDefinitionBindings.Add(definition);
            //        web.RoleAssignments.Add(assignment2);
            //    }
            //    else
            //    {
            //        SPRoleAssignment roleAssignment = web.RoleAssignments.GetAssignmentByPrincipal(spPrincipal);
            //        if (roleAssignment != null)
            //        {
            //            roleAssignment.RoleDefinitionBindings.Remove(definition);
            //            roleAssignment.Update();
            //        }
            //    }
            //}
            //catch (SPException e)
            //{
            //    Logger.Error(e);
            //}
        }
    }

    #endregion

    #region RestrictSiteTemplates web action

    public class RestrictSiteTemplates : WebOperation
    {
        private List<Template> templates = new List<Template>();

        [XmlElement("Template")]
        public List<Template> Templates
        {
            get { return templates; }
            set { templates = value; }
        }

        public override void Execute(SPWeb web)
        {
#if SANDBOX
            Logger.Info("'RestrictSiteTemplates' operation is not available within the sandbox");
            return;
#endif
            Dictionary<string, uint> coll = new Dictionary<string, uint>();
            foreach (Template template in Templates)
            {
                uint lcid = web.Language;
                if (!string.IsNullOrEmpty(template.LCID))
                {
                    uint.TryParse(template.LCID, out lcid);
                }
                coll.Add(template.Value, lcid);
            }

            WebUtil.RestrictSiteTemplates(web, coll);
            web.Update();
        }
    }

#if MOSS
    public class RestrictPageLayouts : WebOperation
    {
        private List<Layout> layouts = new List<Layout>();

        [XmlElement("Layout")]
        public List<Layout> Layouts
        {
            get { return layouts; }
            set { layouts = value; }
        }

        public override void Execute(SPWeb web)
        {
            if (layouts.IsEmpty())
                WebUtil.RestrictPageLayouts(web, layouts.Select(l=> l.Value));
            web.Update();
        }
    }
#endif

    public class Template
    {
        private string lcid;
        private string value;

        [XmlAttribute]
        public string LCID
        {
            get { return lcid; }
            set { lcid = value; }
        }

        [XmlText]
        public string Value
        {
            get { return value; }
            set { this.value = value; }
        }
    }
    public class Layout
    {
        
        private string value;

        [XmlText]
        public string Value
        {
            get { return value; }
            set { this.value = value; }
        }
    }

    #endregion

    #region RestoreWebProperties web action

    public class RestoreWebProperties : WebOperation
    {
        private WebProperies webProperties;

        [XmlElement]
        public WebProperies WebProperties
        {
            get { return webProperties; }
            set { webProperties = value; }
        }

        public override void Execute(SPWeb web)
        {
            if (this.WebProperties != null)
            {
                this.WebProperties.Execute(web);
            }
        }
    }

    public class WebProperies
    {
        private List<WebProperty> properties = new List<WebProperty>();

        [XmlElement("WebProperty")]
        public List<WebProperty> Properties
        {
            get { return properties; }
            set { properties = value; }
        }

        public void Execute(SPWeb web)
        {
            foreach (WebProperty webProperty in Properties)
            {
                if (!string.IsNullOrEmpty(webProperty.Key) && !string.IsNullOrEmpty(webProperty.Value))
                {
                    string key = webProperty.Key;
                    string value = webProperty.Value;
#if !SANDBOX
                    if (web.Properties.ContainsKey(key))
                    {
                        web.Properties[key] = value;
                    }
                    else
                    {
                        web.Properties.Add(key, value);
                    }
#else
                    if (web.AllProperties.ContainsKey(key))
                    {
                        Logger.Info("Updating Property: Key: " + key + ", Value: " + value);
                        web.AllProperties[key] = value;
                    }
                    else
                    {
                        Logger.Info("Adding Property: Key: " + key + ", Value: " + value);
                        web.AddProperty(key, value);
                    }
#endif
                }
            }

#if !SANDBOX
            web.Properties.Update();
#else
            web.AllowUnsafeUpdates = true;
            web.Update();
            web.AllowUnsafeUpdates = false;
#endif
        }
        }
    }

    public class WebProperty
    {
        private string key;
        private string value;

        [XmlAttribute]
        public string Key
        {
            get { return key; }
            set { key = value; }
        }

        [XmlAttribute]
        public string Value
        {
            get { return value; }
            set { this.value = value; }
        }
    }

    #endregion

    #region RestoreCss web action

    public class CustomizedCssFiles : WebOperation
    {
        private List<CustomizedCssFile> files = new List<CustomizedCssFile>();

        [XmlElement("CustomizedCssFile")]
        public List<CustomizedCssFile> Files
        {
            get { return files; }
            set { files = value; }
        }

        public override void Execute(SPWeb web)
        {
            List<string> customizedCssFiles = new List<string>();
            foreach (CustomizedCssFile cssFile in Files)
            {
                if (!string.IsNullOrEmpty(cssFile.Name))
                {
                    customizedCssFiles.Add(cssFile.Name);
                }
            }

            SPFolder rootFolder = web.RootFolder;
            SPFolder cssFolder = null;
            string targetFolder = "_styles";
            foreach (SPFolder subFolder in rootFolder.SubFolders)
            {
                if (subFolder.Name.ToLower(CultureInfo.InvariantCulture) == targetFolder)
                {
                    cssFolder = subFolder;
                    break;
                }
            }

            if (cssFolder == null)
            {
                return;
            }

            foreach (SPFile file in cssFolder.Files)
            {
                if (customizedCssFiles.Contains(file.Name))
                {
                    web.CustomizeCss(file.Name);
                    web.Update();
                }
            }
        }
    }
    public class SetStyling : WebOperation
    {
        [XmlAttribute]
        public string ApplyTheme
        {
            get; set;
        }
        
        [XmlAttribute]
        public string AlternateCss
        {
            get; set;
        }

        [XmlAttribute]
        public string SiteLogoUrl
        {
            get;set;
        }
        public override void Execute(SPWeb web)
        {
            if (!string.IsNullOrEmpty(ApplyTheme))
            {
                web.ApplyTheme(ApplyTheme);
            }
            if (!string.IsNullOrEmpty(AlternateCss))
            {
                web.AlternateCssUrl = AlternateCss;
            }
            if (!string.IsNullOrEmpty(SiteLogoUrl))
            {
                web.SiteLogoUrl = SiteLogoUrl;
            }
            
            web.Update();
        }
    }

    public class CustomizedCssFile
    {
        private string name;

        [XmlAttribute]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
    }

    #endregion


    #region ProvisionLists web action

    public class ProvisionLists : WebOperation
    {
        private List<List> lists = new List<List>();

        [XmlElement("List")]
        public List<List> Lists
        {
            get { return lists; }
            set { lists = value; }
        }

        public override void Execute(SPWeb web)
        {
            var nodes = Lists;
            Provision(nodes, web);
        }

        private static void Provision(IEnumerable<List> nodes, SPWeb web)
        {
            foreach (List listNode in nodes)
            {
                if (string.IsNullOrEmpty(listNode.Title))
                {
                    Logger.Info("Required attribute Title is missing");
                    continue;
                }

                if (string.IsNullOrEmpty(listNode.Template.ToString()))
                {
                    Logger.Info("Required attribute Template is missing");
                    continue;
                }

                SPList newList = web.Lists.TryGetList(listNode.Title);
                if (newList != null)
                {
                    Logger.Info("List already exists, cannot provision");
                    continue;
                }

                web.AllowUnsafeUpdates = true;
                Guid newListId = web.Lists.Add(listNode.Title, listNode.Description, (SPListTemplateType)listNode.Template);
                web.AllowUnsafeUpdates = false;

                newList = web.Lists.GetList(newListId, false);
                listNode.Process(newList);

            }
        }
    }   


    #endregion ProvisionLists web action


    #region ProvisionWebs web action

    public class ProvisionWebs : WebOperation
    {
        private List<Web> webs = new List<Web>();

        [XmlElement("Web")]
        public List<Web> Webs
        {
            get { return webs; }
            set { webs = value; }
        }

        public override void Execute(SPWeb web)
        {
            var nodes = Webs;
            Provision(nodes, web);
        }

        private static void Provision(IEnumerable<Web> nodes, SPWeb web)
        {
            foreach (Web webNode in nodes)
            {
                if (string.IsNullOrEmpty(webNode.Title))
                {
                    Logger.Info("Required attribute Title is missing");
                    continue;
                }

                if (string.IsNullOrEmpty(webNode.Template))
                {
                    Logger.Info("Required attribute Template is missing");
                    continue;
                }

                if (string.IsNullOrEmpty(webNode.Url))
                {
                    Regex cleaner = new Regex(@"[\W]");
                    webNode.Url = cleaner.Replace(webNode.Title, "").ToLower(CultureInfo.InvariantCulture);
                }

                uint lcid = web.Language;
                if (!string.IsNullOrEmpty(webNode.LCID))
                {
                    uint.TryParse(webNode.LCID, out lcid);
                }

                Action<SPWeb> applyActions = w => { if (webNode.Actions.Enabled && webNode.Actions.Operations.Count > 0) ProvisionerRoot.Process(w, webNode.Actions.Operations);  };

                string weburl = webNode.Url;
                if (web.GetSubwebsForCurrentUser().Names.Any(url => string.Equals(url, weburl, StringComparison.OrdinalIgnoreCase)))
                {
                    var existingWeb = web.Webs[weburl];
                    if (webNode.InheritTopNav.HasValue && webNode.InheritTopNav.Value)
                    {
                        existingWeb.Navigation.UseShared = true;
                    }

                    existingWeb.Title = webNode.Title;
                    existingWeb.Description = webNode.Description;
                    existingWeb.Update();

                    if (webNode.UniquePermissions.HasValue)
                    {
                        if (existingWeb.HasUniqueRoleAssignments && !webNode.UniquePermissions.Value)
                            existingWeb.ResetRoleInheritance();
                        else if (!existingWeb.HasUniqueRoleAssignments && webNode.UniquePermissions.Value)
                        {
                            existingWeb.BreakRoleInheritance(true);
                        }
                    }

                    applyActions(existingWeb);

                    Logger.Info("Updated web: " + existingWeb.ServerRelativeUrl);


                    if (webNode.Webs != null && webNode.Webs.Count > 0)
                    {
                        Provision(webNode.Webs, existingWeb);
                    }
                }
                else
                {
                    SPWeb newWeb = null;
                    try
                    {
                        newWeb = web.Webs.Add(weburl, webNode.Title, webNode.Description, lcid,
                                            webNode.Template, webNode.UniquePermissions.HasValue && webNode.UniquePermissions.Value, false);
                    }
                    catch (Exception ex)
                    {
                        Logger.Error("Unable to create web: {0} under {1}\n{2}", weburl, web.ServerRelativeUrl, ex);
                        web.Webs.Delete(weburl);
                    }
                    if (newWeb != null)
                        using (newWeb)
                        {
                            Logger.Info("Created web: " + newWeb.ServerRelativeUrl);

                            if (webNode.InheritTopNav.HasValue && webNode.InheritTopNav.Value)
                            {
                                newWeb.Navigation.UseShared = true;
                                newWeb.Update();
                            }

                            applyActions(newWeb);

                            if (webNode.Webs != null && webNode.Webs.Count > 0)
                            {
                                Provision(webNode.Webs, newWeb);
                            }
                        }

                }
            }
        }

    }

    public class Web
    {
        public class ActionContainer
        {
            public ActionContainer()
            {
                Operations = new List<WebOperation>();
                Enabled = true;
            }

            [XmlElement(typeof(FixupLists))]
            [XmlElement(typeof(ProvisionLists))]
            [XmlElement(typeof(RestrictSiteTemplates))]
            [XmlElement(typeof(RestoreWebProperties))]
            [XmlElement(typeof(CustomizedCssFiles))]
            [XmlElement(typeof(SetStyling))]
            [XmlElement(typeof(ProvisionWebs))]
            [XmlElement(typeof(FixupWebACL))]
#if !SANDBOX
            [XmlElement(typeof(FixupFiles))]
#endif
            public List<WebOperation> Operations { get; set; }
            [XmlAttribute]
            public bool Enabled { get; set; }
        }

        public Web()
        {
            Webs = new List<Web>();
            Actions = new ActionContainer();
        }

        [XmlElement("Actions")]
        public ActionContainer Actions { get; set; }
        
        [XmlElement("Web")]
        public List<Web> Webs { get; set; }

        [XmlAttribute]
        public string Title { get; set; }

        [XmlAttribute]
        public string Description { get; set; }

        [XmlAttribute]
        public string Template { get; set; }

        [XmlAttribute]
        public string Url { get; set; }

        [XmlAttribute]
        public string LCID { get; set; }

        private bool? uniquePermissions;

        [XmlIgnore]
        public bool? UniquePermissions
        {
            get { return uniquePermissions; }
            set { uniquePermissions = value; }
        }

        private bool? inheritTopNav;

        [XmlIgnore]
        public bool? InheritTopNav
        {
            get { return inheritTopNav; }
            set { inheritTopNav = value; }
        }


        [XmlAttribute("UniquePermissions")]
        public string StrUniquePermissions
        {
            get { return GetValue(uniquePermissions); }
            set { SetBool(ref uniquePermissions, value); }
        }

        [XmlAttribute("InheritTopNav")]
        public string StrInheritTopNav
        {
            get { return GetValue(inheritTopNav); }
            set { SetBool(ref inheritTopNav, value); }
        }

        private static string GetValue(bool? value)
        {
            return value.HasValue ? value.Value.ToString() : null;
        }
        private static void SetBool(ref bool? prop, string value)
        {
            if (!string.IsNullOrEmpty(value))
            {
                prop = bool.Parse(value);
            }
            else
                prop = null;
        }
    }

    #endregion

